/*
 * GramaticaView.cpp
 *
 *  Created on: 23/11/2012
 *      Author: vagner
 */

#include "GramaticaView.h"
#include <iomanip>
#include <cstdio>
#include <climits>

namespace tp2ftcView {


const char* opcoes[] = {"(a) Definir variável de partida",
						"(b) Inserir regra",
						"(c) Remover regra",
						"(d) Remover variável",
						"(e) Listar variáveis inúteis",
						"(f) Carregar gramática de um arquivo",
						"(g) Exibir gramática",
						"(q) Sair"};
std::vector<std::string> GramaticaView::OPCOES_MENU(opcoes, opcoes+ NUM_OPCOES_MENU);



GramaticaView::GramaticaView() {
	// TODO Auto-generated constructor stub

}

GramaticaView::~GramaticaView() {
	// TODO Auto-generated destructor stub
}

inline std::string GramaticaView::getEntradaUsuario(const unsigned short & modo){

	std::string entradaUsuario;
	switch (modo) {
		case NO_SKIP_WS:
			std::getline(std::cin,entradaUsuario);
			break;
		case SKIP_WS:
			std::cin >> std::skipws >> entradaUsuario;
			std::cin.clear();
			std::cin.ignore(INT_MAX,'\n');
			break;
		default:
			break;
	}
	//std::cout<<"O usuário informou o valor: " << entradaUsuario << std::endl;

	return entradaUsuario;
}

inline void GramaticaView::showMenuOpcoes()const{

	std::vector<std::string>::iterator itMenu;
	size_t numOpcoesMenu = 0;

	int  largura = getMaiorTamanhoPalavra();
	numOpcoesMenu = OPCOES_MENU.size();

	for(int i = 0; i < int(numOpcoesMenu); i++){

		if( i % 2 == 0){
			std::cout<< std::setw(largura+1)<< std::left<< OPCOES_MENU.at(i);
			}else{
			std::cout<< std::right<< OPCOES_MENU.at(i);
			std::cout<<std::endl;
		}
	}


}

inline int GramaticaView::getMaiorTamanhoPalavra()const{

	int maiorPalavra = OPCOES_MENU.at(0).size();
	std::vector<std::string>::const_iterator itMenu;

	for(itMenu = OPCOES_MENU.begin()+1; itMenu != OPCOES_MENU.end(); itMenu++){

		if( int( (*itMenu).size() ) > maiorPalavra){
			maiorPalavra = (*itMenu).size();
		}
	}
	//std::cout<<"Maior palavra: "<< maiorPalavra << std::endl;
	return maiorPalavra;
}

void GramaticaView::showMenuPrincipal(){

	std::string opcaoUsuario;
	bool exitOpcao = false;


	while (!exitOpcao){
		showMenuOpcoes();
			std::cout<<std::left<<"Opção: ";
			opcaoUsuario = getEntradaUsuario(SKIP_WS);

			if(opcaoUsuario.size() == 1){
				switch (std::tolower(opcaoUsuario.at(0))) {
								case 'a':
									showMenuDefinirVariavelPartida();
									break;
								case 'b':
									showMenuInserirRegra();
									break;
								case 'c':
									showMenuRemoverRegra();
									break;
								case 'd':
									showMenuRemoverVariavel();
									break;
								case 'e':
									showMenuListarVariaveisInuteis();
									break;
								case 'f':
									showMenuCarregarGramatica();
									break;
								case 'g':
									showGramatica();
									break;
								case 'q':
									exitOpcao = true;
									break;
								default:
									std::cout<<"Opção inválida!!!!!!!!"<<std::endl;
									exitOpcao = false;
									break;
							}

			}else{
				std::cout<<"Opção Inválida"<<std::endl;
			}
	}

}


void GramaticaView::showMenuDefinirVariavelPartida(void){

	bool voltaOpcao = false;
	bool novaVariavelOpcao = false;
	std::string opcaoUsuario;
	std::string feedbackUsuario;

	while(!voltaOpcao){
		std::cout<<"Variável de Partida: ";
		opcaoUsuario =  getEntradaUsuario(NO_SKIP_WS);

		feedbackUsuario = gramaticaControl.defineVariavelPartida(opcaoUsuario);
		std::cout << feedbackUsuario << std::endl;
		showGramatica();
		do{
			std::cout<<"Deseja informar uma nova variável de partida (S/N)? ";
			opcaoUsuario = getEntradaUsuario(SKIP_WS);
			if(opcaoUsuario.size() > 1){
				std::cout<<"Opção Invalida!"<<std::endl;
			}else{
				switch (std::tolower(opcaoUsuario.at(0))) {
					case 'n':
						novaVariavelOpcao = true;
						voltaOpcao = true;
						break;
					case 's':
						novaVariavelOpcao = true;
						break;
					default:
						std::cout<<"Opção Inválida!!!"<<std::endl;
						novaVariavelOpcao = false;
						break;
				}
			}

		}while (!novaVariavelOpcao);
	}

}

void GramaticaView::showMenuInserirRegra(void){

	bool voltaOpcao = false;
	bool novaRegraOpcao = false;
	std::string opcaoUsuario;
	std::string feedbackUsuario;
		while(!voltaOpcao){
			std::cout<<"Regra: ";
			opcaoUsuario =  getEntradaUsuario(NO_SKIP_WS);

			feedbackUsuario = gramaticaControl.insereRegra(opcaoUsuario);
			std::cout << feedbackUsuario << std::endl;
			showGramatica();

			do{
				std::cout<<"Deseja inserir uma nova regra (S/N)? ";
				opcaoUsuario = getEntradaUsuario(SKIP_WS);
				if(opcaoUsuario.size() > 1){
					std::cout<<"Opção Invalida!"<<std::endl;
				}else{
					switch (std::tolower(opcaoUsuario.at(0))) {
						case 'n':
							novaRegraOpcao = true;
							voltaOpcao = true;
							break;
						case 's':
							novaRegraOpcao = true;
							break;
						default:
							std::cout<<"Opção Inválida!!!"<<std::endl;
							novaRegraOpcao = false;
							break;
					}
				}

			}while (!novaRegraOpcao);
		}


}


void GramaticaView::showMenuListarVariaveisInuteis(void){

	std::vector<variavel> variaveis;
	std::vector<variavel> variaveisNaoInuteis;
	std::vector<variavel> variavelInutil;
	std::cout<<"V' = ";
	variaveis = gramaticaControl.recuperaVariaveisGeramPalavras();
	showVariaveis(variaveis);

	std::cout <<"V'' = ";
	variaveisNaoInuteis = gramaticaControl.recuperaVariaveisAlcancaveis(variaveis);
	showVariaveis(variaveisNaoInuteis);

	std::cout <<"Inútil = ";
	variavelInutil = gramaticaControl.recuperaVariaveisInuteis(variaveisNaoInuteis);
	showVariaveis(variavelInutil);
}

void GramaticaView::showMenuRemoverRegra(void){

	bool voltaOpcao = false;
	bool removerNovamente = false;
	std::string opcaoUsuario;
	std::string feedbackUsuario;

		while(!voltaOpcao){
			std::cout<<"Regra: ";
			opcaoUsuario =  getEntradaUsuario(NO_SKIP_WS);

			feedbackUsuario = gramaticaControl.removeRegra(opcaoUsuario);
			std::cout<<feedbackUsuario << std::endl;
			showGramatica();

			do{
				std::cout<<"Deseja remover outra regra (S/N)? ";
				opcaoUsuario = getEntradaUsuario(SKIP_WS);
				if(opcaoUsuario.size() > 1){
					std::cout<<"Opção Invalida!"<<std::endl;
				}else{
					switch (std::tolower(opcaoUsuario.at(0))) {
						case 'n':
							removerNovamente = true;
							voltaOpcao = true;
							break;
						case 's':
							removerNovamente = true;
							break;
						default:
							std::cout<<"Opção Inválida!!!"<<std::endl;
							removerNovamente = false;
							break;
					}
				}

			}while (!removerNovamente);
		}


}

void GramaticaView::showMenuRemoverVariavel(void){
	bool voltaOpcao = false;
		bool removerNovamente = false;
		std::string opcaoUsuario;
		std::string feedbackUsuario;

		while(!voltaOpcao){
			std::cout<<"Variavel: ";
			opcaoUsuario =  getEntradaUsuario(NO_SKIP_WS);

			feedbackUsuario =  gramaticaControl.removeVariavel(opcaoUsuario);
			std::cout<< feedbackUsuario<<std::endl;
			showGramatica();
			do{
				std::cout<<"Deseja remover outra variável (S/N)? ";
				opcaoUsuario = getEntradaUsuario(SKIP_WS);
				if(opcaoUsuario.size() > 1){
					std::cout<<"Opção Invalida!"<<std::endl;
				}else{
					switch (std::tolower(opcaoUsuario.at(0))) {
						case 'n':
							removerNovamente = true;
							voltaOpcao = true;
							break;
						case 's':
							removerNovamente = true;
							break;
						default:
							std::cout<<"Opção Inválida!!!"<<std::endl;
							removerNovamente = false;
							break;
					}
				}

			}while (!removerNovamente);
		}
}

void GramaticaView::showGramatica()const{
	std::string variavelPartida;
	std::vector<regra> regrasCadastradas;
	std::vector<regra>::const_iterator itRegras;
	std::vector<std::string>::const_iterator itStr;


	variavelPartida = gramaticaControl.recuperaVariavelPartida();
	regrasCadastradas = gramaticaControl.recuperaRegras();

	std::cout<< "GRAMATICA:"<<std::endl;

	if(variavelPartida.empty()){
		std::cout << "Variável de partida: NÃO DEFINIDA" << std::endl;
	}else{
		std::cout << "Variável de partida: "<< variavelPartida << std::endl;
	}

	if(regrasCadastradas.empty()){
		std::cout<< "Regras: Não há regras cadastradas!"<< std::endl;
	}else{
		std::cout << "Regras: " <<std::endl;
		for(itRegras = regrasCadastradas.begin(); itRegras != regrasCadastradas.end(); itRegras++){
			std::cout << itRegras->ladoEsquerdo << " -> ";
			for(itStr = itRegras->ladoDireito.begin(); itStr != itRegras->ladoDireito.end(); itStr++){
				std::cout << (*itStr);
			}
			std::cout << std::endl;
		}
	}

}

void GramaticaView::showVariaveis(const std::vector<variavel>& variaveis) const{

	std::vector<variavel>::const_iterator itVariavel;

	std::cout<<"{";

	for(itVariavel = variaveis.begin(); itVariavel != variaveis.end();itVariavel++){
		if(itVariavel == (variaveis.end()-1)){
			std::cout<<(*itVariavel);
		}else{
			std::cout<<(*itVariavel)<<",";
		}
	}
	std::cout<<"}"<<std::endl;

}

void GramaticaView::showMenuCarregarGramatica(void){
	std::string opcaoUsuario;
	std::string feedbackUsuario;
	std::string regraLida;
	std::cout << "Informe o nome do arquivo: ";
	opcaoUsuario =  getEntradaUsuario(NO_SKIP_WS);

	abreArquivoEntrada(opcaoUsuario);

	while((regraLida = leiaProxLinha()) != "EOF"){
		gramaticaControl.insereRegra(regraLida);
	}

	showGramatica();
}

} /* namespace tp2ftcView */

